﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Project.cs" company="Mar3ek">
//   http://mar3ek.wordpress.com
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;

namespace LighthouseNET
{
    using System;
    using System.Xml.Linq;

    /// <summary>
    /// Class representing a LightHouse project
    /// </summary>
    public class Project
    {
        #region Fields

        #endregion

        /// <summary>
        /// Initializes a new instance of the <see cref="Project"/> class.
        /// </summary>
        /// <param name="isArchived">Whether the project should be archived.</param>
        /// <param name="name">The project name.</param>
        /// <param name="isPublic">Whether the project should be public.</param>
        /// <exception cref="ArgumentNullException">Name is null.</exception>
        public Project(bool isArchived, string name, bool isPublic)
        {
            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentNullException("name");
            }

            IsArchived = isArchived;
            IsPublic = isPublic;
            Name = name;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Project"/> class. 
        /// </summary>
        /// <param name="isArchived">
        /// Whether the project is archived.
        /// </param>
        /// <param name="createdAt">
        /// The time of creation.
        /// </param>
        /// <param name="defafultAssignedUserId">
        /// The defafult assigned user id.
        /// </param>
        /// <param name="defaultMilestoneId">
        /// The default milestone id.
        /// </param>
        /// <param name="description">
        /// The description.
        /// </param>
        /// <param name="descriptionHtml">
        /// The description HTML.
        /// </param>
        /// <param name="license">
        /// The license.
        /// </param>
        /// <param name="name">
        /// The project name.
        /// </param>
        /// <param name="id">
        /// The project id.
        /// </param>
        /// <param name="openTicketsCount">
        /// The open tickets count.
        /// </param>
        /// <param name="permalink">
        /// The permalink.
        /// </param>
        /// <param name="isPublic">
        /// Whether the project is public.
        /// </param>
        /// <param name="updatedAt">
        /// The last time the project was updated.
        /// </param>
        /// <param name="states">Ticket states</param>
        private Project(
            bool isArchived,
            DateTime createdAt,
            int defafultAssignedUserId,
            int defaultMilestoneId,
            string description,
            string descriptionHtml,
            string license,
            string name,
            int id,
            int openTicketsCount,
            string permalink,
            bool isPublic,
            DateTime updatedAt,
            IList<string> states)
        {
            IsArchived = isArchived;
            CreatedAt = createdAt;
            DefaultAssignedUserId = defafultAssignedUserId;
            DefaultMilestoneId = defaultMilestoneId;
            Description = description;
            DescriptionHtml = descriptionHtml;
            License = license;
            Name = name;
            Id = id;
            OpenTicketsCount = openTicketsCount;
            Permalink = permalink;
            IsPublic = isPublic;
            UpdatedAt = updatedAt;
            TicketStates = new ReadOnlyCollection<string>(states);
        }

        #region Properties

        /// <summary>
        /// Gets a value indicating whether this project is archived.
        /// </summary>
        /// <value>
        /// <c>true</c> if this project is archived; otherwise, <c>false</c>.
        /// </value>
        public bool IsArchived { get; private set; }

        /// <summary>
        /// Gets the time of creation.
        /// </summary>
        public DateTime CreatedAt { get; private set; }

        /// <summary>
        /// Gets the default assigned user id.
        /// </summary>
        public int DefaultAssignedUserId { get; private set; }

        /// <summary>
        /// Gets the default milestone id.
        /// </summary>
        public int DefaultMilestoneId { get; private set; }

        /// <summary>
        /// Gets the description.
        /// </summary>
        public string Description { get; private set; }

        /// <summary>
        /// Gets the HTML description.
        /// </summary>
        public string DescriptionHtml { get; private set; }

        /// <summary>
        /// Gets the license type.
        /// </summary>
        public string License { get; private set; }

        /// <summary>
        /// Gets the project name.
        /// </summary>
        public string Name { get; private set; }

        /// <summary>
        /// Gets the project id.
        /// </summary>
        public int Id { get; private set; }

        /// <summary>
        /// Gets the open tickets count.
        /// </summary>
        public int OpenTicketsCount { get; private set; }

        /// <summary>
        /// Gets the permalink.
        /// </summary>
        public string Permalink { get; private set; }

        /// <summary>
        /// Gets a value indicating whether this project is public.
        /// </summary>
        /// <value>
        ///   <c>true</c> if this project is public; otherwise, <c>false</c>.
        /// </value>
        public bool IsPublic { get; private set; }

        /// <summary>
        /// Gets the  last time the project was updated.
        /// </summary>
        public DateTime UpdatedAt { get; private set; }

        /// <summary>
        /// Gets the ticket states.
        /// </summary>
        public ReadOnlyCollection<string> TicketStates { get; private set; } 

        #endregion

        /// <summary>
        /// Parses the specified element.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <returns>Parsed project</returns>
        /// <exception cref="ArgumentNullException">Element is null.</exception>
        internal static Project Parse(XElement element)
        {
            if (element == null || element.IsEmpty)
            {
                throw new ArgumentNullException("element");
            }

            // ReSharper disable PossibleNullReferenceException
            var isArchived = bool.Parse(element.Element("archived").Value);

            var createdAt = Globals.DateTimeFromLighthouse(element.Element("created-at").Value);
            int defaultAssignedUserId;
            int.TryParse(element.Element("default-assigned-user-id").Value, out defaultAssignedUserId);
            int defaultMilestoneId;
            int.TryParse(element.Element("default-milestone-id").Value, out defaultMilestoneId);
            var description = element.Element("description").Value;
            var descriptionHtml = element.Element("description-html").Value;
            var id = int.Parse(element.Element("id").Value);
            var license = element.Element("license").Value;
            var name = element.Element("name").Value;
            var openTicketsCount = int.Parse(element.Element("open-tickets-count").Value);
            var permalink = element.Element("permalink").Value;
            var isPublic = bool.Parse(element.Element("public").Value);
            var updatedAt = Globals.DateTimeFromLighthouse(element.Element("updated-at").Value);
            var states = new List<string>();
            var openStates = ParseStates(element.Element("open-states").Value);
            var closedStates = ParseStates(element.Element("closed-states").Value);
            states.AddRange(openStates);
            states.AddRange(closedStates);
            
            // ReSharper restore PossibleNullReferenceException)
            return new Project(
                isArchived, 
                createdAt,
                defaultAssignedUserId,
                defaultMilestoneId,
                description,
                descriptionHtml,
                license,
                name,
                id,
                openTicketsCount,
                permalink,
                isPublic,
                updatedAt,
                states);
        }

        /// <summary>
        /// Parses the states.
        /// </summary>
        /// <param name="states">The states.</param>
        /// <returns>Collection of states</returns>
        private static IEnumerable<string> ParseStates(string states)
        {
            var result = new Collection<string>();
            foreach (var line in states.Split("\n\r".ToCharArray(), StringSplitOptions.RemoveEmptyEntries))
            {
                var label = new StringBuilder();
                foreach (var c in line)
                {
                    if (c == '/' || c == '#')
                    {
                        if (label.Length > 0)
                        {
                            result.Add(label.ToString());
                        }
                        
                        break;
                    }

                    label.Append(c);
                }
            }

            return result;
        }
    }
}
